/**************************************************************
*
* M3D_VOL.H
*
* Volume functionality
**************************************************************/

//
// Volume types
//
enum {
   M3D_VOL_TYPE_UNDEF = 0,
   M3D_VOL_TYPE_BOX,
   M3D_VOL_TYPE_PRISM,
   M3D_VOL_TYPE_SPHERE,
   M3D_VOL_TYPE_OBB,
   M3D_VOL_TYPE_CAPSULE,
   M3D_VOL_TYPE_CYL,
   M3D_VOL_TYPE_MAX,
};


enum {
   M3D_VOL_FACE_LEFT = 0, // -Z
   M3D_VOL_FACE_FRONT,    //  X
   M3D_VOL_FACE_RIGHT,    //  Z
   M3D_VOL_FACE_BACK,     // -X
   M3D_VOL_FACE_BOTTOM,   // -Y
   M3D_VOL_FACE_TOP       //  Y
};

//
// Sphere class
//
class m3dSPHERE {
public:  
   m3dV     org;
   float    radius;

   m3dSPHERE (void) {};
   m3dSPHERE (m3dV _org, float _rad) : org(_org), radius(_rad) {}

   BOOL  IsBelongPoint   (const m3dV *v);
   BOOL  IsectCyl        (m3dV *vFrom, m3dV *dir, float distMax, float radius, m3dV *vClosest, float *dist, int *faceNmb);
   int   IsectRay        (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *vClosest, float *dist, int *faceNmb = NULL);
   int  _IsectRay        (m3dV *rayOrg, m3dV *rayDir, float *t);
   int   DistPoint       (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb) const;
   void  Calc            (m3dV *pointList, int nPoint);
   BOOL  Check           (void) const;
   void  CalcEnclSph     (class m3dSPHERE *pSph);
   BOOL  CheckEnclSph    (class m3dSPHERE *pSph);
   void  GetCenter       (m3dV *center) const         { *center = org; }
   void  GetFace         (int faceNmb, m3dPOLY *poly, m3dV *norm = NULL) { ASSERT(FALSE); }
   void  GetFaceCenter   (int faceNmb, m3dV *center) { ASSERT(FALSE); }
   void  Expand          (float value);
   int   QualPlane       (m3dV *planeOrg, m3dV *planeNorm);
   float GetSize         (void) { return 2*radius; }

   float          GetRadius      (void) const { return radius; }

private:
   void _FindMaxLSeg             (m3dV *pointList, int nPoint, int *n1, int *n2);
};

//
// Box class
//
class m3dBOX {
public:
   enum QUAL_INT {
      QUAL_INT_OUTSIDE,
      QUAL_INT_ISECT,
   };
public:
   m3dV  bll;        // back-low-left
   m3dV  fur;        // front-up-right

   m3dBOX  (void) {};
   m3dBOX  (class m3dOBB* obb);

   BOOL  Check           (void) const;
   BOOL  IsBelongPoint   (const m3dV *v) const;
   int   DistPoint       (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb);
   int   DistPoint_2     (m3dV *p, float distMax_2, m3dV *vClosest, float *dist_2, int *faceNmb);
   void  Expand          (float value);
   BOOL  IsectCyl        (m3dV *vFrom, m3dV *dir, float distMax, float radius, m3dV *vClosest, float *dist, int *faceNmb);
   int   IsectRay        (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *vClosest, float *dist, int *faceNmb = NULL);
   void  Calc            (m3dV *pointList, int nPoint)    { Calc(pointList, nPoint, 0.f); return; }
   void  CalcEnclSph     (class m3dSPHERE *pSph);
   BOOL  CheckEnclSph    (class m3dSPHERE *pSph);
   void  GetCenter       (m3dV *center) const;
   void  GetFace         (int faceNmb, m3dPOLY *poly, m3dV *norm = NULL);
   void  GetFaceCenter   (int faceNmb, m3dV *center);
   int   QualPlane       (m3dV *planeOrg, m3dV *planeNorm);
   float GetSize         (void) const { return m3dDist(&bll, &fur); }

   // box-specific functions
   void  GetVertices          (m3dV vList[8]) const;
   void  GetFacePoint         (int faceNmb, m3dV *point);
   void  GetEdgePoints        (int edgeNmb, m3dV *point1, m3dV *point2);
   void  GetEdgePointIndices  (int edgeNmb, int  *ind1,   int  *ind2);
   void  GetSurface           (m3dPOLY polyList[6]);
   void  GetDim               (m3dV *dimBox)    { m3dSubtractVector(&fur, &bll, dimBox); }
   float GetDistToPointSq     (const m3dV & point) const;
   float GetDistToPoint       (const m3dV & point) const { return m3dSqrt(GetDistToPointSq(point)); }

   // bbox calculation functions
   void  CalcStart            (void);
   void  CalcUpdate           (const m3dV *v);
   void  CalcUpdate           (const m3dV *v, float radius);
   void  CalcUpdate           (const m3dV *pointList, int nPoint);
   void  CalcUpdate           (const m3dBOX *pB);
   void  CalcUpdateXZ         (m2dV *v);
   void  CalcUpdateXZ         (m2dV *pointList, int nPoint);

   void  Calc                 (m3dV *pointList, int nPoint, float sideMinLen);
   void  CalcXZ               (m3dV *pointList, int nPoint, float sideMinLen = 0.f);
   void  CalcXZ               (m2dV *pointList, int nPoint, float sideMinLen = 0.f);
   void  MakePointPoint       (m3dV *p1, m3dV *p2);
   void  Translate            (m3dMATR &matr);

   void  Scale                (float x, float y, float z);
   float GetArea              (void);
   float GetVolume            (void);

   // point/ray/poly intersection functions
   bool  IsIsectBox           (const m3dBOX *pBB) const;
   BOOL  IsIsectOBB           (m3dOBB *pOBB);
   int   IsIsectPoly          (m3dPOLY *poly, m3dV *norm);
   int   IsIsectPolyXZ        (m3dPOLY *poly, m3dV *norm);
   BOOL  IsBelongPointXZ      (m3dV *v);

   int   QualifyBox           (const m3dBOX &box) const {return ClipBox(box, NULL);} // created method: 28.02.2006
   int   ClipBox              (const m3dBOX &box, m3dBOX *pBoxClip) const ;          // created method: 28.02.2006
   BOOL  ClipLSeg2            (m3dV *_vFrom, m3dV *_vTo, m3dV &vFrom, m3dV &vTo);    // created method: 28.02.2006
   BOOL  ClipLSeg             (m3dV *vFrom, m3dV *vTo, m3dV *v1, m3dV *v2);
   void  Transform            (m3dMATR *pLCS2WCS, class m3dBOX *bb)  const;
   void  Transform            (m3dMATR *pLCS2WCS, class m3dOBB *obb) const;
   void  Transform2OBB_ADV    (m3dMATR *pLCS2WCS, class m3dOBB *obb) const;
   void  ConvToOBB            (m3dOBB *pOBB) const;

   static QUAL_INT QualifyBoxINTEGER    (const m3dBOX &box1, const m3dBOX &box2); // FRESH method: 21.09.2010
private:
   BOOL IsBelongPointFace     (int indFace, m3dV *p);
};


//
// OBB class
//
class m3dOBB {
   m3dV  org;
   m3dV  vx, vy, vz;
   float sx, sy, sz;
public:

   m3dOBB  () {};
   m3dOBB  (m3dMATR &matr, float _sx, float _sy, float _sz);
   m3dOBB  (const m3dV &_org, const m3dV &_vx, const m3dV &_vy, const m3dV &_vz, const m3dV &_size);

   m3dV*       GetOrg   (void)         { return &org; }
   const m3dV* GetOrg   (void) const   { return &org; }
   m3dV*       GetDirX  (void)         { return &vx;  }
   const m3dV* GetDirX  (void) const   { return &vx;  }
   m3dV*       GetDirY  (void)         { return &vy;  }
   const m3dV* GetDirY  (void) const   { return &vy;  }
   m3dV*       GetDirZ  (void)         { return &vz;  }
   const m3dV* GetDirZ  (void) const   { return &vz;  }
   float&      GetSizeX (void)         { return sx;   }
   const float&GetSizeX (void) const   { return sx;   }
   float&      GetSizeY (void)         { return sy;   }
   const float&GetSizeY (void) const   { return sy;   }
   float&      GetSizeZ (void)         { return sz;   }
   const float&GetSizeZ (void) const   { return sz;   }

   BOOL IsectCylExact (m3dV *vrom, m3dV *dir, float height, float radius); // new one (14.02.08)
   void ClearSize() {sx = sy = sz = 0.f;}
   void SetSize(float _sx, float _sy, float _sz) {sx = _sx; sy = _sy; sz = _sz;}

             int   DistPoint_2     (m3dV *p, float distMax_2, m3dV *vClosest, float *dist_2, int *faceNmb);

   BOOL  IsBelongPoint   (const m3dV *v) const;
   BOOL  IsectCyl        (m3dV *vFrom, m3dV *dir, float distMax, float radius, m3dV *vClosest, float *dist, int *faceNmb);
   int   IsectRay        (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *vClosest, float *dist, int *faceNmb = NULL);
   int   DistPoint       (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb);
   void  Calc            (m3dV *pointList, int nPoint);
   BOOL  Check           (void) const;
   void  CalcEnclSph     (class m3dSPHERE *pSph);
   BOOL  CheckEnclSph    (class m3dSPHERE *pSph);
   void  GetCenter       (m3dV *center) const;
   void  GetFace         (int faceNmb, m3dPOLY *poly, m3dV *norm = NULL);
   void  GetFaceCenter   (int faceNmb, m3dV *center);
   void  Expand          (float value);
   int   QualPlane       (m3dV *planeOrg, m3dV *planeNorm);
   float GetSize         (void) { return m3dSqrt(m3dPow2(sx)+m3dPow2(sy)+m3dPow2(sz)); }

   void  CalcStart               (m3dV *vx, m3dV *vy, m3dV *vz);
   void  CalcUpdate              (m3dV *pointList, int nPoint = 1);
   void  CalcUpdate              (m3dOBB *pB);

   // OBB-specific functions
   BOOL  IsIsectOBB              (m3dOBB *obb);
   BOOL  IsIsectBox              (m3dBOX *box);

   void  GetVertices             (m3dV vList[8]) const;
   void  GetSurface              (m3dPOLY polyList[6]);

   void  GetMatrW2L              (m3dMATR *pMatr, m3dMATR *pMatrInv = NULL, m3dBOX *bbLCS = NULL) const;
   void  CalcEnclBox             (m3dBOX *bb) const;
   void  GetBoxLCS               (m3dBOX *bbLCS) const;
   void  ApplyMatrix             (const m3dMATR *matr, m3dOBB *obb) const;
   void  SetSizeRelative         (float _sx, float _sy, float _sz);
};


//
// Class prism
//
class m3dPRISM {
public:
   m2dPOLY  polyBase;   // base polygon
   float    yLo, yHi;   // vertical bounds

   m3dPRISM (void) {};

   BOOL  IsBelongPoint   (const m3dV *v);
   BOOL  IsectCyl        (m3dV *vFrom, m3dV *dir, float distMax, float radius, m3dV *vClosest, float *dist, int *faceNmb);
   int   IsectRay        (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *vClosest, float *dist, int *faceNmb = NULL);
   int   DistPoint       (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb);
   void  Calc            (m3dV *pointList, int nPoint);
   BOOL  Check           (void) const;
   void  CalcEnclSph     (class m3dSPHERE *pSph);
   BOOL  CheckEnclSph    (class m3dSPHERE *pSph);
   void  GetCenter       (m3dV *center) const;
   void  GetFace         (int faceNmb, m3dPOLY *poly, m3dV *norm = NULL);
   void  GetFaceCenter   (int faceNmb, m3dV *center);
   void  Expand          (float value);
   int   QualPlane       (m3dV *planeOrg, m3dV *planeNorm);
   float GetSize         (void) { return m3dSqrt(m2dDist_2(&polyBase[0], &polyBase[1]) + m3dPow2(yHi - yLo)); }

   // prism-specific functions
   void     GetVertices       (m3dV vList[8]) const;
   void     GetSurface        (m3dPOLY polyList[6]);
   BOOL     Expand            (m3dV *vExpandForw, m3dV *vExpandBack);
   BOOL     Expand            (float expandForw, float expandRight, float expandUp);

   void     GetMatrW2L        (m3dMATR *pMatrW2L);
   void     GetMatrL2W        (m3dMATR *pMatrL2W);
   void     ConvToOBB         (class m3dOBB  *pOBB);

private:
   // bounding prism computation functions
   float   _MakeBPrismRect    (m3dV *vertList, int nVert, float angle,
      float *xMin, float *xMax, float *yMin, float *yMax);
   void    _MakeBPrismPolyBase(float angle,
      float xMin, float xMax, float yMin, float yMax);
};

//
// Class cylinder - @@ inherit from m3dVOL
//
class m3dCYL {
public:
   m3dV  from, to;
   float radius;

   m3dCYL(void) : radius(-1.f) {}
   m3dCYL(m3dV  _from, m3dV _to, float _radius) : from(_from), to(_to), radius(_radius) {}
};

//
// capsule class
//
class m3dCAPSULE {
public:
   m3dV     from, to;
   float    radius;

   m3dCAPSULE ();
   m3dCAPSULE (const m3dV &_from, const m3dV &_to, float _radius);

   BOOL  IsBelongPoint  (const m3dV *v);
   BOOL  IsectCyl       (m3dV *vFrom, m3dV *dir, float distMax, float radCyl, m3dV *vClosest, float *dist, int *faceNmb);
   int   IsectRay       (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *vClosest, float *dist, int *faceNmb);
   int   DistPoint      (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb);
   void  Calc           (m3dV *pointList, int n);
   BOOL  Check          (void) const;
   void  CalcEnclSph    (class m3dSPHERE *pSph);
   BOOL  CheckEnclSph   (class m3dSPHERE *pSph);
   void  GetCenter      (m3dV *center) const;
   void  GetFace        (int faceNmb, m3dPOLY *poly, m3dV *norm = NULL);
   void  GetFaceCenter  (int faceNmb, m3dV *center);
   void  Expand         (float value);
   int   QualPlane      (m3dV *planeOrg, m3dV *planeNorm);
   float GetSize        (void);

};

//
// Global variables
//
extern m3dV  m3dBoxNorm[6];

//
// Global functions
//
extern BOOL m3dIsBelongPointCone  (m3dV *point,
                                   m3dV *coneOrg, m3dV *coneDir,
                                   float radForw, float angle);

extern BOOL m3dIsBelongPointDblCone(const m3dV *point,
                                    const m3dV *coneOrg, const m3dV *coneDir,
                                    float radIn, float radOut, 
                                    float angleIn, float angleOut);

extern BOOL m3dIsBelongPointCirCone(m3dV *point,
                                    m3dV *coneOrg, m3dV *coneDir,
                                    float radForw, float radBack, float angle);

void m3dComputeOBBFromPoints(int count, const m3dV* pPoints, m3dOBB* result);


//
// End-of-file 'M3D_VOL.H'
//
